#include <windows.h>
#include <time.h>
#include <sys/timeb.h>
#include "pThreads/pthread.h"
#include "pThreads/semaphore.h"
#include "Utils/listKeyClass.h"
#include <vector>
#include "CDb.h"
#include "CSemaphore.h"
#include "CGlobal.h"
#include "Utils/CWindowClass.h"
#include "CConsole.h"


//std::vector<struct RecvKey> CGlobal::connectList;
unsigned char CGlobal::serverName[16];
char * CGlobal::serverMessage;
float CGlobal::expRate;
float CGlobal::dropRate;

CSemaphore* CGlobal::charListSemaphore;
//sem_t CGlobal::connectListSemaphore;

CSemaphore* CGlobal::charRemovalListSemaphore;
std::vector<CChar*> CGlobal::charRemovalList;

CSemaphore* CGlobal::NPCListSemaphore;

T2List<unsigned int,CChar*>* CGlobal::m_charListById=NULL;
T2List<CStringKeyCI,CChar*>* CGlobal::m_charListByName=NULL;
T2List<long long,CChar*>* CGlobal::m_charListByIpUdpport=NULL;
T2List<unsigned short,CChar*>* CGlobal::m_charListByIpTargId=NULL;

std::vector<struct connections> CGlobal::lobbyConList;
CSemaphore* CGlobal::lobbyConListSemaphore;

T2List<unsigned int,CChar*>* CGlobal::getCCharList(void)
{
	return CGlobal::m_charListById;
}

void CGlobal::checkLists(void)
{
	if (CGlobal::m_charListById==NULL)
	{
		//Create Lists
		CGlobal::m_charListById = new T3ListProtectedDebugKeepLog<unsigned int,CChar*,T2AVLList>();
		CGlobal::m_charListByName = new T3ListProtectedDebugKeepLog<CStringKeyCI,CChar*,T2AVLList>();
		CGlobal::m_charListByIpUdpport = new T3ListProtectedDebugKeepLog<long long,CChar*,T2AVLList>();
		CGlobal::m_charListByIpTargId = new T3ListProtectedDebugKeepLog<unsigned short,CChar*,T2AVLList>();
	}
}

CChar* CGlobal::getCCharById(unsigned int charId)
{
	CGlobal::checkLists();
	return CGlobal::m_charListById->find(charId);
}

CChar* CGlobal::getCCharByName(char* charName)
{
	CGlobal::checkLists();
	return CGlobal::m_charListByName->find(CStringKeyCI(charName));
}

CChar* CGlobal::getCCharByIpUdpport(ULONG ip, u_short port)
{
	CGlobal::checkLists();
	long long ipUdpPort = ip + (((long long)port)<<32);
	return CGlobal::m_charListByIpUdpport->find(ipUdpPort);
}

CChar* CGlobal::getCCharByTargId(unsigned short targId)
{
	CGlobal::checkLists();
	/*
	//Another way without using a separate index:
	CChar* foundChar = NULL;
	CChar* currChar = NULL;
	for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) 
	{
		currChar = CGlobal::getCCharAt(k);
		if(currChar->getTargId() == targId){
			foundChar=currChar;
			break;
		}
	}
	return foundChar;
	*/
	return CGlobal::m_charListByIpTargId->find(targId);
}

CChar* CGlobal::getCCharAt(unsigned int position)
{
	CGlobal::checkLists();
	return CGlobal::m_charListById->at(position);
}

unsigned int CGlobal::getCCharListSize()
{
	CGlobal::checkLists();
	return CGlobal::m_charListById->getSize();
}

char CGlobal::addCChar(CChar* newChar)
{
	CConsole::outDebOnly("Adding char %s (%d) to the global list",newChar->getName(),newChar->getId());
	CGlobal::checkLists();
	CGlobal::m_charListById->pushBack(newChar->getId(),newChar);
	CGlobal::m_charListByName->pushBack(CStringKeyCI(newChar->getName()),newChar);
	CConsole::getOutputWindow()->setValue("Players online",CGlobal::m_charListById->getSize());
	return 0;
}

char CGlobal::removeCChar(CChar* delChar)
{
	CGlobal::checkLists();
	CGlobal::m_charListById->removeByKey(delChar->getId());
	CGlobal::m_charListByName->removeByKey(CStringKeyCI(delChar->getName()));
	delChar->setUdpPort(0); //this will remove the char from the ipUdpList!
	delChar->setTargId(0);
	CConsole::getOutputWindow()->setValue("Players online",CGlobal::m_charListById->getSize());
	return 0;
}

char CGlobal::linkCCharWithUdpPort( ULONG ip, u_short port, CChar* newChar )
{
	CGlobal::checkLists();
	long long ipUdpPort = ip + (((long long)port)<<32);
	return CGlobal::m_charListByIpUdpport->pushFront(ipUdpPort,newChar);
}

char CGlobal::unlinkCCharWithUdpPort( ULONG ip, u_short port )
{
	CGlobal::checkLists();
	long long ipUdpPort = ip + (((long long)port)<<32);
	return CGlobal::m_charListByIpUdpport->removeByKey(ipUdpPort);
}

char CGlobal::linkCCharWithTargId( unsigned short targId, CChar* newChar )
{
	#ifdef _DANTALION_DEBUG
	CConsole::outPlain("Linking Char with targId: %d - %s",targId,newChar->getName());
	#endif

	CGlobal::checkLists();
	return CGlobal::m_charListByIpTargId->pushFront(targId,newChar);
}

char CGlobal::unlinkCCharWithTargId( unsigned short targId )
{
	#ifdef _DANTALION_DEBUG
	CChar* tempchar = NULL;
	CGlobal::m_charListByIpTargId->find(targId);
	CConsole::outPlain("Unlinking Char with targId: %d - %s",targId, ( (tempchar != NULL) ? tempchar->getName() : "CharNotInList") );
	#endif

	CGlobal::checkLists();
	return CGlobal::m_charListByIpTargId->removeByKey(targId);
}

void CGlobal::exitCharList(void)
{
	CGlobal::checkLists();
	CGlobal::m_charListById->unlock();
	CGlobal::m_charListByName->unlock();
	CGlobal::m_charListByIpUdpport->unlock();
	CGlobal::charListSemaphore->unlock();
	return;
}

void CGlobal::waitCharList(void) 
{
	CGlobal::checkLists();
	//CConsole::outTime("1");
	CGlobal::m_charListById->lock(); // Char Remove locked here
	//CConsole::outTime("2");
	CGlobal::m_charListByName->lock();
	//CConsole::outTime("3");
	CGlobal::m_charListByIpUdpport->lock();
	//CConsole::outTime("4");
	CGlobal::charListSemaphore->lock();
	//CConsole::outTime("5");
	return;
}

void CGlobal::exitCharRemovalList(void)
{
	CGlobal::charRemovalListSemaphore->forceUnlock();
	return;
}

void CGlobal::waitCharRemovalList(void) 
{
	CGlobal::charRemovalListSemaphore->forceLock();
	return;
}

void CGlobal::lockLobbyConList(void)
{
	CGlobal::lobbyConListSemaphore->lock();
	return;
}

void CGlobal::unlockLobbyConList(void)
{
	CGlobal::lobbyConListSemaphore->unlock();
	return;
}